package org.jef.core.util;

/**
 * @version 1.0
 * @description: 分布式锁
 * @author: ChengZhi 0336
 * @create: 2024-10-16 19:18
 **/

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 基于redis的简单分布式锁.
 */
public class DistributedLock implements Lock {

    private final String LOCK_SUCCESS = "OK";

    private final Long RELEASE_SUCCESS = 1L;

    private final static String PRE_LOCK = "lock:";

    private String lockKey;

    public DistributedLock(String key) {
        this.lockKey = PRE_LOCK + key;
    }

    @Override
    public void lock() {
        try {
            if (!acquire()) {
                throw new RuntimeException("acquire timeouted");
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("lock failed", e);
        }
    }


    @Override
    public void lockInterruptibly() throws InterruptedException {
        if (!acquire()) {
            throw new RuntimeException("acquire timeouted");
        }
    }

    @Override
    public boolean tryLock() {
        try {
            return acquire();
        } catch (InterruptedException e) {
            throw new RuntimeException("lock failed", e);
        }
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return acquire();
    }

    @Override
    public void unlock() {
        releaseDistributedLock();
    }

    @Override
    public Condition newCondition() {
        throw new RuntimeException("unsupported method");
    }


    /**
     * 尝试获取锁 有限次数的重试
     *
     * @return
     * @throws InterruptedException
     */
    private Boolean acquire() throws InterruptedException {
        Integer i = 0;
        do {
            i++;
            boolean locked = tryGetDistributedLock();
            if (locked) {
                return true;
            } else {
                Thread.sleep(100L);
            }
        } while (i < 20);
        return false;
    }

    /**
     * 尝试获取锁
     *
     * @return
     */
    private Boolean tryGetDistributedLock() {
        int millisecondsToExpire = 2;
        Long threadId = Thread.currentThread().getId();
        return RedisUtils.getLock(this.lockKey, threadId.toString(), millisecondsToExpire);
    }


    /**
     * 释放分布式锁
     *
     * @return 是否释放成功
     */
    private Boolean releaseDistributedLock() {
        Long threadId = Thread.currentThread().getId();
        return RedisUtils.releaseLock(lockKey, threadId.toString());
    }
}
